﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
using EpiNEXT.Services;
using EpiNEXT.Services.Collections;
using EpiNEXT.Services.Fields;

namespace EpiNEXT.Applications
{
    public class FormViewModel : FormBaseViewModel
    {
        #region Members
        private static object _pagesLock = new object();
        private string _searchString = String.Empty;
        private ObservableCollection<PageViewModel> _pageCollection = new ObservableCollection<PageViewModel>();
        #endregion

        #region Properties

        public string SearchString
        {
            get { return this._searchString; }
            set
            {
                if (!SearchString.Equals(value))
                {
                    this._searchString = value;
                    RaisePropertyChanged("SearchString");
                }
            }
        }

        public bool IsRelatedForm
        {
            get { return Form.IsRelatedForm; }
            private set
            {
                Form.IsRelatedForm = value;
                RaisePropertyChanged("IsRelatedForm");
            }
        }

        public int MaxRecords
        {
            get { return Form.MaxRecords; }
            private set
            {
                Form.MaxRecords = value;
                RaisePropertyChanged("MaxRecords");
            }
        }

        public bool HasUnsavedChanges
        {
            get { return Form.HasUnsavedChanges; }
            private set
            {
                Form.HasUnsavedChanges = value;
                RaisePropertyChanged("HasUnsavedChanges");
            }
        }

        public Form ParentForm
        {
            get { return Form.ParentForm; }
            private set
            {
                Form.ParentForm = value;
                RaisePropertyChanged("ParentForm");
            }
        }

        public string Description
        {
            get { return (Form as Form).Description; }
            private set
            {
                (Form as Form).Description = value;
                RaisePropertyChanged("Description");
            }
        }

        public ICollectionView PagesView { get; set; }

        public int Version
        {
            get { return (Form as Form).Version; }
            private set
            {
                (Form as Form).Version = value;
                RaisePropertyChanged("Version");
            }
        }

        public double PageWidth
        {
            get { return (Form as Form).PageWidth; }
            private set
            {
                (Form as Form).PageWidth = value;
                RaisePropertyChanged("PageWidth");
            }
        }

        public double PageHeight
        {
            get { return (Form as Form).PageHeight; }
            private set
            {
                (Form as Form).PageHeight = value;
                RaisePropertyChanged("PageHeight");
            }
        }

        public double PageMarginTop
        {
            get { return (Form as Form).PageMarginTop; }
            private set
            {
                (Form as Form).PageMarginTop = value;
                RaisePropertyChanged("PageMarginTop");
            }
        }

        public double PageMarginLeft
        {
            get { return (Form as Form).PageMarginLeft; }
            private set
            {
                (Form as Form).PageMarginLeft = value;
                RaisePropertyChanged("PageMarginLeft");
            }
        }

        public Dictionary<ComboBoxField, CodeListViewModel> ListFieldLookup { get; set; }

        #endregion // Properties

        #region Constructor
        public FormViewModel(DataEntryInstrument form, ProjectViewModel projectVM)
            : base(form, projectVM)
        {
            this.ListFieldLookup = new Dictionary<ComboBoxField, CodeListViewModel>();
            this._pageCollection = new ObservableCollection<PageViewModel>();

            RaisePropertyChanged("RecordsView");

            if (Form is Form)
            {
                foreach (Page page in (Form as Form).Pages)
                {
                    PageViewModel pageVM = new PageViewModel(page, this);
                    _pageCollection.Add(pageVM);
                }

                RaisePropertyChanged("PagesView");
            }

            foreach (IField field in this.Fields)
            {
                // To prevent using the same VM for each field, and therefore sync'ing 
                // list values across fields (which is BAD!) create new VMs. TODO: Look into
                // a better solution...

                if (field is YesNoField)
                {
                    YesNoField yesNoField = field as YesNoField;

                    CodeList codeList = null;

                    if (yesNoField.IncludeUnknown == true)
                    {
                        codeList = this.Project.CodeListYNU;
                    }
                    else
                    {
                        codeList = this.Project.CodeListYN;
                    }

                    CodeListViewModel vm = new CodeListViewModel(codeList);
                    ListFieldLookup.Add(yesNoField, vm);
                }
                else if (field is ComboBoxField) // must be an else-if since a YesNoField inherits from ComboBoxField
                {
                    ComboBoxField comboBoxField = field as ComboBoxField;

                    CodeList codeList = this.Project.GetCodeListById(comboBoxField.ListSource.Id);
                    CodeListViewModel vm = new CodeListViewModel(codeList);
                    ListFieldLookup.Add(comboBoxField, vm);
                }
            }
        }
        #endregion Constructor

        #region Methods
        /// <summary>
        /// Forces a re-validation of all data in the current record
        /// </summary>
        public void ForceValidationOnCurrentRecord()
        {
            (this.RecordsView.CurrentItem as Record).Validate();
        }

        /// <summary>
        /// Gets the level at which this form appears in a relational hierarchy.
        /// </summary>
        /// <returns>int; represents the level at which the form appears in the hierarchy. 0 = top-most parent, 1 = child of top-most parent, 2 = grandchild of top-most parent, etc.</returns>
        public int GetRelationalLevel()
        {
            return this.Form.GetRelationalLevel();
        }

        /// <summary>
        /// Enables collection sync on the key collection properties for the form view model, allowing them to be accessed on non-UI threads.
        /// </summary>
        public override void InitalizeCollectionSynchronization()
        {
            base.InitalizeCollectionSynchronization();

            PagesView = new CollectionViewSource { Source = _pageCollection }.View;
            System.Windows.Data.BindingOperations.EnableCollectionSynchronization(PagesView, _pagesLock);
        }

        /// <summary>
        /// Gets the appropriate code list view model for a given combo box field
        /// </summary>
        /// <param name="comboBoxField">The combo box field whose list values should be returned</param>
        /// <returns>The view model representing the list values for the combo box field</returns>
        public CodeListViewModel GetCodeListViewModelForField(ComboBoxField comboBoxField)
        {
            //if (comboBoxField is YesNoField)
            //{
            //    YesNoField ynField = comboBoxField as YesNoField;
            //    if (ynField.IncludeUnknown)
            //    {
            //        return this.ProjectVM.CodeListYesNoUnknownViewModel;
            //    }
            //    else
            //    {
            //        return this.ProjectVM.CodeListYesNoViewModel;
            //    }
            //}

            if (ListFieldLookup.ContainsKey(comboBoxField))
            {
                return ListFieldLookup[comboBoxField];
            }

            return null;
        }

        public object GetCurrentRecordValue(IField field)
        {
            Record currentRecord = this.RecordsView.CurrentItem as Record;
            if (currentRecord == null) return null;
            object value = currentRecord.GetFieldData(field);
            return value;
        }

        /// <summary>
        /// Deletes a record from the form's current set of records
        /// </summary>
        /// <param name="record">The record to remove</param>
        /// <returns>bool; whether the operation was successful</returns>
        public bool DeleteRecord(Record record)
        {
            return Form.DeleteRecord(record);
        }

        /// <summary>
        /// Adds a new record to the form's set of records
        /// </summary>
        /// <returns>record; the record that was added</returns>
        public Record AddRecord()
        {
            Record newRecord = null;

            if (this.IsRelatedForm)
            {
                newRecord = Form.AddRecord(System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString(), (ParentFormVM.RecordsView.CurrentItem as Record).Id);
            }
            else
            {
                newRecord = Form.AddRecord(System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString());
            }

            //if (this.ParentFormVM != null)
            //{
            //    Record parentRecord = this.ParentFormVM.RecordsView.CurrentItem as Record; // this shouldn't be null, otherwise how would we get here?
            //    newRecord.ForeignKey = parentRecord.Id; // attach FKEY
            //}

            //RaisePropertyChanged("RecordsView");

            return newRecord;
        }
        #endregion // Methods

        #region Commands
        public ICommand SearchRecords { get { return new RelayCommand(SearchRecordsExecute); } }
        private void SearchRecordsExecute()
        {
            try
            {
                string searchString = SearchString.Trim();//.ToLower();

                if (!String.IsNullOrEmpty(searchString))
                {
                    RecordsView.Filter = new Predicate<object>
                        (
                            record =>
                                ((Record)record).ContainsSearchValue(searchString)
                        );
                }
                else
                {
                    RecordsView.Filter = null;
                }
            }
            catch (Exception ex)
            {
                //System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString() + ": " +
                //"Searching for " + SearchString + " in cases list generated exception with message: " + ex.Message);
                RecordsView.Filter = null;
                SearchString = String.Empty;
            }
        }
        #endregion // Commands
    }
}
